#ifndef __PRETTY_PRINTS_DOT_HPP__
#define __PRETTY_PRINTS_DOT_HPP__
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
namespace std {
    class CommaInt {
    private:
        const uint64_t n;
        const uint32_t w;
    public:
        CommaInt( const uint64_t _n, const uint32_t _w ) : n( _n ), w( _w ) {}
        friend ostream & operator << ( ostream & os, const CommaInt & ci ) {
    
            // a copy of the value
            uint64_t x = ci.n;
    
            // build a vector containing values [0,999]
            // these values will be comma-joined in the printout
            vector< uint64_t > vals;
            while( x > 999 ) {
                uint64_t r = x % 1000ULL;
                vals.push_back( r );
                x /= 1000;
            }
            
            // do some padding to align numeric columns
            uint32_t nchars = 3 + 4 * vals.size();
            while( nchars < ci.w ) {
                os << " ";
                nchars++;
            }
            
            // beautifully write out the number
            os << SETDEC( 3 ) << x;
    
            while( vals.size() ) {
                uint64_t r = vals.back();
                vals.pop_back();
                os << "," << ZERODEC( 3 ) << r;
            }
            return os;
        }
    };
    class PrettyInt {
    private:
        vector< string > markers;
        const uint32_t w;
        uint64_t nbytes;
        double   fbytes;
        void init( void ) {
            markers.push_back( " " );
            markers.push_back( "K" );
            markers.push_back( "M" );
            markers.push_back( "G" );
            markers.push_back( "T" );
            markers.push_back( "P" );
            markers.push_back( "E" );
            markers.push_back( "Z" );
            markers.push_back( "Y" );
            markers.push_back( "?" );
        }
    public:
        PrettyInt( const uint64_t _b, const uint32_t _w ) : w( _w ) {
            nbytes = _b;
            fbytes = double( nbytes );
            init();
        }
        PrettyInt( const double _b, const uint32_t _w ) : w( _w ) {
            fbytes = _b;
            nbytes = uint64_t( fbytes );
            init();
        }
        friend ostream & operator << ( ostream & os, const PrettyInt & pb ) {
    
            double fbytes = double( pb.fbytes );
            
            uint32_t i = 0;
            
            while( fbytes > 1000.0 ) {
                fbytes /= 1000.0;
                i++;
            }
            if( i >= pb.markers.size() ) {
                i = pb.markers.size() - 1;
            }
            
            os << SETFIX( 6, 2 ) << fbytes << pb.markers[i] << " ( " << CommaInt( pb.nbytes, pb.w ) << " )";
            // os << SETFIX( 6, 2 ) << fbytes << pb.markers[i] << " (" << SETDEC( pb.w ) << pb.nbytes << ")";
            
            return os;
        }
    }; 
    class PrettyBytes {
    private:
        vector< string > markers;
        const uint32_t w;
        uint64_t nbytes;
        double   fbytes;
        void init( void ) {
            markers.push_back( "B " );
            markers.push_back( "kB" );
            markers.push_back( "MB" );
            markers.push_back( "GB" );
            markers.push_back( "TB" );
            markers.push_back( "PB" );
            markers.push_back( "EB" );
            markers.push_back( "ZB" );
            markers.push_back( "YB" );
            markers.push_back( "?B" );
        }
    public:
        PrettyBytes( const uint64_t _b, const uint32_t _w ) : w( _w ) {
            nbytes = _b;
            fbytes = double( nbytes );
            init();
        }
        PrettyBytes( const double _b, const uint32_t _w ) : w( _w ) {
            fbytes = _b;
            nbytes = uint64_t( fbytes );
            init();
        }
        friend ostream & operator << ( ostream & os, const PrettyBytes & pb ) {
    
            double fbytes = double( pb.fbytes );
            
            uint32_t i = 0;
            
            while( fbytes > 1024.0 ) {
                fbytes /= 1024.0;
                i++;
            }
            if( i >= pb.markers.size() ) {
                i = pb.markers.size() - 1;
            }
            
            os << SETFIX( 7, 2 ) << fbytes << pb.markers[i] << " (" << CommaInt( pb.nbytes, pb.w ) << " )";
            // os << SETFIX( 7, 2 ) << fbytes << pb.markers[i] << " (" << SETDEC( pb.w ) << pb.nbytes << ")";
            
            return os;
        }
    };
    class PrettyTime {
        const double sec;
    public:
        PrettyTime( const double _sec ) : sec( _sec ) {}
        friend ostream & operator << ( ostream & os, const PrettyTime & pt ) {
            uint64_t nhrs;
            uint64_t nmin;
            uint64_t nsec;
            
            nsec = uint64_t( pt.sec );
            nhrs = nsec / 3600ULL;
            nsec = nsec % 3600ULL;
            nmin = nsec / 60ULL;
            nsec = nsec % 60ULL;

            os << SETDEC( 3 ) << nhrs << "h " << SETDEC( 2 ) << nmin << "m " << SETDEC( 2 ) << nsec << "s";
            return os;            
        }
    };
}
#endif
